package org.aeon.gamechatnest.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.aeon.gamechatnest.common.Constant;
import org.aeon.gamechatnest.common.enumeration.LoginStatus;
import org.aeon.gamechatnest.common.properties.UserProperties;
import org.aeon.gamechatnest.common.response.ResultBuild;
import org.aeon.gamechatnest.dto.SecurityUserDto;
import org.aeon.gamechatnest.dto.UserCacheDto;
import org.aeon.gamechatnest.dto.UserLoginDto;
import org.aeon.gamechatnest.dto.UserVo;
import org.aeon.gamechatnest.mapper.UserMapper;
import org.aeon.gamechatnest.pojo.User;
import org.aeon.gamechatnest.service.UserService;
import org.aeon.gamechatnest.util.*;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author: MissingAeon
 * @date: 2024/5/9 14:01
 * @description:
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private UserProperties userProperties;

    @Autowired
    private JwtUtil jwtUtil;

    /**
     * 用户登录
     *
     * @param user
     * @return
     */
    @Override
    public boolean login(UserLoginDto user) {
        // 分割密码
        String password = user.getPassword();
        user.setPassword(null);
        // 实现用户登录逻辑
        UsernamePasswordAuthenticationToken usernameAuthentication =
                new UsernamePasswordAuthenticationToken(user, password);
        Authentication authenticate = authenticationManager.authenticate(usernameAuthentication);
        SecurityContextHolder.getContext().setAuthentication(authenticate);

        // 存在 生成一个token, 存在redis中
        SecurityUserDto securityUser = (SecurityUserDto) authenticate.getPrincipal();
        UserCacheDto userCache = securityUser.getUserCache();
        String token = jwtUtil.genAccessToken(securityUser.getUsername());

        // 设置默认状态
        userCache.setLoginStatus(LoginStatus.ONLINE);
        userCache.setAuthority(new HashSet<>(0));

        // TODO 设置过期时间
        redisCache.setCacheObject(Constant.USER_TOKEN_CACHE_PREFIX + securityUser.getUsername(), userCache,
                3600, TimeUnit.SECONDS);

        UserVo userVo = new ModelMapper().map(userCache.getUser(), UserVo.class);
        userVo.setToken(token);

        // 返回
        ResultBuild.result().data(userVo).message("登录成功！");
        return true;
    }

    @Override
    public void logout(String token) {
        String username = jwtUtil.getUserFromToken(token);
        redisCache.deleteObject(Constant.USER_TOKEN_CACHE_PREFIX + username);
        SecurityContextHolder.clearContext();
    }

    @Override
    public boolean validate(UserLoginDto user) {
        return false;
    }

    @Override
    public User findByJson(String json) {
        UserLoginDto userLoginDto = JsonUtil.string2Obj(json, UserLoginDto.class);
        Objects.requireNonNull(userLoginDto);
        if (StringUtils.isNotEmpty(userLoginDto.getUsername())) {
            try {
                String[] strings = userLoginDto.getUsername().split(Constant.ACCOUNT_SEPARATE_CHAR, 2);
                User user = userMapper.selectById(Long.parseLong(strings[1]));
                if (user != null && user.getNickname().equals(strings[0])) {
                    return user;
                }
            } catch (NumberFormatException | ArrayIndexOutOfBoundsException ignored) {
            }
        } else {
            return userMapper.getByIndex(userLoginDto);
        }
        return null;
    }

    @Override
    public boolean register(User user) {
        // TODO 查询手机号或邮箱是否重复
        // 设置创建时间
        user.setCreateTime(new Date());
        // 设置默认昵称
        user.setNickname(StringUtils.selectNonEmpty(user.getNickname(), userProperties.getDefaultNickname()));
        // 保存加密密码
        user.setPassword(passwordEncoder.encode(
                StringUtils.selectNonEmpty(user.getPassword(), userProperties.getDefaultPassword())));
        return userMapper.insert(user) > 0;
    }

    @Override
    public User find() {
        return SecurityUtil.getUser();
    }

    @Override
    public UserVo findVo(Long userId) {
        User user = userMapper.selectById(userId);
        if (Objects.nonNull(user)) {
            UserVo userVo = new ModelMapper().map(user, UserVo.class);
            userVo.setStatus(loginStatus(userVo));
            return userVo;
        }
        return null;
    }

    @Override
    public boolean isExist(Long userId) {
        return userMapper.isExist(userId);
    }

    @Override
    public List<UserVo> findByIds(List<Long> userIds) {
        List<UserVo> vos = userMapper.getByIds(userIds);
        vos.forEach(userVo -> {
            // 是否在线
            userVo.setStatus(loginStatus(userVo));
        });
        return vos;
    }

    @Override
    public LoginStatus loginStatus(Long userId, String nickname) {
        UserCacheDto userCacheDto = redisCache.getCacheObject(
                Constant.USER_TOKEN_CACHE_PREFIX + nickname + Constant.ACCOUNT_SEPARATE_CHAR + userId);
        if (Objects.isNull(userCacheDto)) return LoginStatus.OFFLINE;
        return userCacheDto.getLoginStatus();
    }

    @Override
    public LoginStatus loginStatus(UserVo userVo) {
        return loginStatus(userVo.getUserId(), userVo.getNickname());
    }

    @Override
    public LoginStatus loginStatus(User user) {
        return loginStatus(user.getUserId(), user.getNickname());
    }
}
